from Token import *
from LexicalError import *
from Constants import *

class lexico:
	def __init__(self, input=""):
		self.setInput(input)

	def setInput(self, input):
		self.input = input
		self.setPosition(0)
	
	def nextToken(self):
		if not self.hasInput(): return 0

		start = self.position

		state = 0
		oldState = 0
		endState = -1
		end = -1

		while self.hasInput():

			oldState = state
			state = self.nextState(self.nextChar(), state);

			if state < 0:
				break

			else:
				if self.tokenForState(state) >= 0:
					endState = state
					end = self.position

		if endState < 0 or self.tokenForState(oldState) == -2: # or (self.tokenForState(endState) == 23 and not self.input[start:end] in SPECIAL_CASES_KEYS):
			raise LexicalError(SCANNER_ERROR[oldState], start)

		self.position = end

		token = self.tokenForState(endState)

		if token == 0:
			return self.nextToken()
		else:
			lexeme = self.input[start:end]
			token = self.lookupToken(token, lexeme)
			return Token(token, lexeme, start)

	def nextState(self, c, state):
		next = SCANNER_TABLE[state][ord(c)]
		return next

	def tokenForState(self, state):
		if state >= 0 and state < STATES_COUNT:
			return TOKEN_STATE[state]

		return -1

	def lookupToken(self, base, key):
		start = SPECIAL_CASES_INDEXES[base]
		end = SPECIAL_CASES_INDEXES[base+1]-1

		while start <= end:
			half = (start+end)/2
			current = SPECIAL_CASES_KEYS[half]
		
			if current == key:
				return SPECIAL_CASES_VALUES[half]
			elif current < key:
				start = half+1
			else:
				end = half-1

		return base
	
	def hasInput(self):
		return self.position < len(self.input)

	def nextChar(self):
		if self.hasInput():
			self.position += 1
			return self.input[self.position-1]
		else:
			return '-1';

	def setPosition(self, position):
		self.position = position
